package com.toobigtofail.enitities
{
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.Joints.b2RevoluteJointDef;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2Fixture;
	import Box2D.Dynamics.b2World;
	
	import com.toobigtofail.shapes.IShape;
	import com.toobigtofail.shapes.Line;
	import com.toobigtofail.shapes.Wedge;
	import com.toobigtofail.utility.Util;
	
	import fabulaexmachina.box2fp.Box2DEntity;
	import fabulaexmachina.box2fp.Box2DShapeBuilder;
	import fabulaexmachina.box2fp.Box2DUtils;
	import fabulaexmachina.box2fp.Box2DWorld;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.Endian;
	
	import net.flashpunk.FP;
	import net.flashpunk.utils.Draw;
	
	public class CavernWall extends Box2DEntity
	{
		public static const TYPE:String = "cavern_wall";
		
		private static const Y_INTERVAL:Number = 30;
		private static const BUFFER:Number = 4;
		private static const LINE_WIDTH:uint = 3;
		
		public static const LOWEST_X:Number = LINE_WIDTH +2;
		public static const HIGHEST_X:Number = FP.screen.width - LINE_WIDTH -2;
		
		public static const LEFT:uint = 0;
		public static const RIGHT:uint = 1;
		
		// Drawing
		//screen-sized transparent pre-processing buffer
		private var _preprocess:BitmapData;
		//screen size and location
		private var _screenRect:Rectangle;
		
		private var _fillDirection:uint = LEFT;
		
		private var _lastDestroyPoint:Number = 0;
		
		private var _line:Line;
		private var _minX:Number = 0;
		private var _maxX:Number = FP.screen.width;
		
		public var addEnemy:Boolean = false;
		
		private var _color:uint = 0xFFFFFFFF;
		private var _speed:Number = 1;
		
		public function CavernWall(min_x:Number, max_x:Number, fillDirection:uint = LEFT)
		{
			_fillDirection = fillDirection;
			
			// Set the range in which we can generate the wall on the x-axis
			this.minX = min_x;
			this.maxX = max_x;
			
			// Create a set of vertices to represent the cavern wall (with a little buffer so it goes off the screen and never shows a gap)
			var verts:Array = new Array();
			for (var y:Number = 0; y < FP.screen.height + (Y_INTERVAL*BUFFER); y += Y_INTERVAL) {
				// Make sure our lines are an even Y_INTERVAL in height
				if (y > FP.screen.height  + (Y_INTERVAL*BUFFER)) y = FP.screen.height + (Y_INTERVAL*BUFFER);
				// add the vertice
				verts.push(new b2Vec2(Util.random(minX, maxX), y));
			}
			
			// Create the box2d line made up of the vertices.
			// We calculate the relative position of the vertices since the line expects all of the
			// vertice positions to be relative to the line's world center, and in meters rather than pixels.
			_line = new Line(verts[0].x, 0, Line.absoluteToRelative(verts[0].x, 0, verts), b2Body.b2_kinematicBody);
			
			// Construct this thang
			super(_line.X, _line.Y, _line.Width, _line.Height, _line.b2Type);
			type = TYPE;
			
			// Set up the preprocess buffer for rendering the lines that make up the walls
			_screenRect = new Rectangle(0, 0, FP.width, FP.height);
			_preprocess = new BitmapData(_screenRect.width, _screenRect.height, true, 0);
		}
		
		override public function added():void {
			super.added();
			
			// Start the wall moving up (so it looks like everything else is falling)
			body.SetLinearVelocity(new b2Vec2(0, -_speed));
		}
		
		override public function buildShapes(friction:Number, 
											 density:Number, restitution:Number,
											 group:int, category:int, collmask:int):void { 
			_line.buildShape(body);
		}
		
		override public function update():void {
			super.update();
			
			// If the wall has floated up far enough past the top of the screen,
			// destroy the top fixture and create a new one at the bottom
			//
			if (y < _lastDestroyPoint-(Y_INTERVAL*2)) {
				_lastDestroyPoint = y - (y%Y_INTERVAL);
				
				// Remove a segment from the beginning
				_line.removeSegment(body, 0);
				
				// Add the new segment to the bottom
				var lastSeg:b2Vec2 = Line.absoluteToRelativeVec(x, y, new b2Vec2(Util.random(_minX, _maxX), FP.screen.height + (Y_INTERVAL*BUFFER)));
				_line.addSegment(body, lastSeg);
				if(addEnemy)
				{
					var point1:b2Vec2 = Line.relativeToAbsoluteVec(x,y,_line.vertices[_line.vertices.length-2]);
					var point2:b2Vec2 = Line.relativeToAbsoluteVec(x,y,_line.vertices[_line.vertices.length-1]);
					//absPoint1.x = absPoint1.x * (FP.world as Box2DWorld).scale;
					//absPoint1.x = absPoint2.x * (FP.world as Box2DWorld).scale;
					//var point1:b2Vec2 = Line.absoluteToRelativeVec(x, y, absPoint1);
					//var point2:b2Vec2 = Line.absoluteToRelativeVec(x, y, absPoint2);
						
					var spikesSlope:Number = (point1.y - point2.y) / (point1.x - point2.x);
					
					var segLength:Number = Math.sqrt(Math.pow(point1.x - point2.x,2) + Math.pow(point1.y - point2.y, 2));
				/*
					var midPoint:Point = new Point((point1.x - point2.x)/2 + point2.x,(point1.y - point2.y)/2 + point2.y);
					
					var spikeWidth:Number = segLength/6;
					spikeWidth = spikeWidth / (FP.world as Box2DWorld).scale;
					var spikeHeight:Number = Math.random()*spikeWidth + spikeWidth;
					*/
					var spikeAngleRads:Number = Math.atan2((point1.y - point2.y), (point1.x - point2.x));
					spikeAngleRads = -spikeAngleRads-Math.PI;
					var spikesX:Number = 0;
					var spikesY:Number = 0;
					var spikeHeight:int = segLength/6 + (Math.random()*(segLength/6));
					var flippin:Boolean = false;
					if(_fillDirection == LEFT)
					{
						//spikesX = spikeHeight;
						spikesX = +6;
						if(spikesSlope > 0)
						{
							spikesY = -15;
						}
					}
					else 
					{
						flippin = true;
						spikesX = -6;
						if(spikesSlope < 0)
						{
							spikesY = -15;
						}
						//spikesX = -spikeHeight;
					}
					point1.x += spikesX;
					point2.x += spikesX;
					point1.y += spikesY;
					point2.y += spikesY;
					var zeroPoint:b2Vec2 = new b2Vec2;
					zeroPoint.x = 0;
					zeroPoint.y = 0;
					var relativePoint:b2Vec2 = new b2Vec2;
					relativePoint.x = point2.x - point1.x;
					relativePoint.y = point2.y - point1.y;
		
					var vertArray:Array = new Array(zeroPoint, Line.absoluteToRelativeVec(0,0,relativePoint));
					
					
					var enemy:Enemy = new Enemy(new Line(point1.x, point1.y, vertArray, b2Body.b2_kinematicBody),spikeAngleRads, flippin);
					FP.world.add(enemy);
						
					
					
					
					//var spikeJointDef:b2RevoluteJointDef = new b2RevoluteJointDef;
					//spikeJointDef.Initialize(this.body, enemy.body, point1);
				
					addEnemy = false;
				}
			}
		}
		
		
		override public function render():void {
			_preprocess.fillRect(_screenRect, 0);
			Draw.setTarget(_preprocess);
			var drawVerts:Array = Line.relativeToAbsolute(x,y,_line.vertices);
			var p1:b2Vec2;
			var p2:b2Vec2;
			
			// Fill any gaps at the top
			if (drawVerts.length) {
				p1 = drawVerts[0];
				if (p1.y > 0) {
					Draw.linePlus(p1.x, 0, p1.x, p1.y, _color, 1, LINE_WIDTH);
				}
			}
			
			// Draw the cave wall
			for (var i:int = 0; i < drawVerts.length-1; i++) {
				p1 = drawVerts[i];
				p2 = drawVerts[i+1];
				
				Draw.linePlus(p1.x, p1.y, p2.x, p2.y, _color, 1, LINE_WIDTH);
			}
			
			// Fill any gaps at the bottom
			if (p2 && p2.y < FP.screen.height) {
				Draw.linePlus(p2.x, p2.y, p2.x, FP.screen.height, _color, 1, LINE_WIDTH);
			}
			
			// Fill outside of the cave wall
			if (LEFT == _fillDirection) {
				_preprocess.floodFill(0, 1, _color);
			}
			else {
				_preprocess.floodFill(FP.screen.width-1, 1, _color);
			}
			
			super.render();
			
			FP.buffer.copyPixels(_preprocess, _screenRect, new Point(0,0));
		}
		
		public function shift(pixels:Number):void {
			minX = minX + pixels;
			maxX = maxX + pixels;
		}
		
		public function set color(c:uint):void { _color = c; }
		public function set speed(s:Number):void { 
			_speed = s;
			if (body) body.SetLinearVelocity(new b2Vec2(0, -_speed));
		}
		public function get speed():Number { return _speed; }
		public function set maxX(x:Number):void { _maxX = x > HIGHEST_X ? HIGHEST_X : x; }
		public function set minX(x:Number):void { _minX = x < LOWEST_X ? LOWEST_X : x; }
		
		public function get minX():Number { return _minX; }
		public function get maxX():Number { return _maxX; }

	}
}